package jp.android_group.attender.android.ui;

/*
 * Copyright (c) 2009, "Japan Android Group Attender Team"
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Timer;
import java.util.TimerTask;

import jp.android_group.attender.android.R;
import jp.android_group.attender.android.entity.Attendee;
import jp.android_group.attender.android.entity.Event;
import jp.android_group.attender.android.entity.ServerEntry;
import jp.android_group.attender.android.net.IServerInterface;
import jp.android_group.attender.android.net.ServerInterfaceFactory;

import org.apache.http.client.HttpResponseException;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.DialogInterface.OnDismissListener;
import android.inputmethodservice.InputMethodService;
import android.inputmethodservice.KeyboardView.OnKeyboardActionListener;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.PowerManager;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.View.OnClickListener;
import android.view.View.OnKeyListener;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.TextView.OnEditorActionListener;

/**
 * 出席者リスト表示画面
 * 
 * @author Keiji Ariyama <keiji_ariyama@c-lis.co.jp>
 * 
 */
public class AttendeeListActivity extends BaseActivity
implements OnClickListener, OnEditorActionListener {
	
	public static final String LOG_TAG = "AttendeeListActivity";
	public static final boolean DEBUG_FLG = true;

	// 定数：リクエストコード
	public static final int REQUEST_CODE = 0x583426fa;

	// 定数：結果コード - 設定アクティビティ起動のため終了する
	public static final int RESULT_LAUNCH_LOGIN_SETTING = 0x1245afa;

	// 定数：再試行の回数
	public static final int RETRY_NUMBER = 4;
	public static final int RETRY_WAIT_TIME = 2;

	IServerInterface mServerInterface = null;
	ServerEntry mServerEntry = null;
	private volatile boolean mCancelFlg = false;
	private final Timer mTimer = new Timer();

	// リストアダプタ
	private ListAdapter mListAdapter = null;

	private Dialog mDialogConfirm = null;
	private ProgressBar mPbConfirmRemainTime = null;

	// イベント情報
	private Event mEventData = null;

	// 出席予定者リスト
	private final HashMap<String, Attendee> mAttendeeList = new HashMap<String, Attendee>();

	private AlertDialog mAlertDialog = null;
	/**
	 * 参加者リストをサーバーに送信
	 */
	private Runnable mUploadAttendeeList = new Runnable() {
		public void run() {
	
			synchronized(mDb) {
				if(mModeOffline == false) {
					for (int i = 0; i < RETRY_NUMBER; i++) {
	
						boolean retryFlg = false;
						try {
							synchronized (mAttendeeList) {
								// アップロード
								HashMap<String, Attendee> list = mServerInterface.uploadAttendeeData(mEventData, mAttendeeList);
								if(list != null) {
									mAttendeeList.clear();
									mAttendeeList.putAll(list);
								}
							}
						} catch (final HttpResponseException e) {
							retryFlg = true;
							showToastException(e);
	
						} catch (final IOException e) {
							retryFlg = true;
							showToastException(e);
						}
	
						if (retryFlg == false) {
							break;
						}
					}
				}
	
				// データの保存
				saveAttendeeData();
				listReload(mAttendeeList);
	
			} // synchronized(mDb)
		}
	};
	// 画面の要素
	private ListView mLVAttendeeList = null;

	// 音声再生用プレイヤー
	private MediaPlayer mSoundPlayerSad = null;

	private ProgressDialog mProgressDialog = null;
	private PowerManager.WakeLock mWakeLock = null;
	private Handler mHandler = new Handler() {
		public void handleMessage(Message msg) {
			switch (msg.what) {

			case HANDLE_SHOW_PROGRESS_DIALOG:
				synchronized (mProgressDialog) {
					mProgressDialog
							.setMessage(getText(R.string.retrieving_attendee_list));
					mProgressDialog.show();
				}
				break;

			case HANDLE_DISMISS_PROGRESS_DIALOG:
				synchronized (mProgressDialog) {
					if (mProgressDialog.isShowing())
						mProgressDialog.dismiss();
				}
				break;

			case HANDLE_SET_RETRYING_MESSAGE_PROGRESS_DIALOG:
				mProgressDialog.setMessage(getText(R.string.retrying));
				break;

			case HANDLE_RELOAD_LIST:
				// リストの更新
				if(mListAdapter.list.size() == 0) {
					Toast.makeText(AttendeeListActivity.this, getText(R.string.attendee_not_found), Toast.LENGTH_LONG).show();
				}
				mListAdapter.notifyDataSetChanged();
				break;

			case HANDLE_SHOW_ATTENDEE_SENDING_PROGRESS:
				initProgressDialog(HANDLE_SHOW_ATTENDEE_SENDING_PROGRESS);
				mProgressDialog.show();
				break;

			case HANDLE_SHOW_CANNOT_SENT_ALERT:
				initAlertDialog(HANDLE_SHOW_CANNOT_SENT_ALERT);
				mAlertDialog.show();
				break;
				
			case HANDLE_START_EVENT_SELECT_ACTIVITY:
				startEventSelectActivity();
				break;
				
			case HANDLE_UPDATE_TITLE:
				updateTitle();
				break;
			};
		}
	};

	private static final int HANDLE_UPDATE_TITLE = 0x49847;
	private static final int HANDLE_SHOW_PROGRESS_DIALOG = 0x9835;
	private static final int HANDLE_DISMISS_PROGRESS_DIALOG = 0x573457;
	private static final int HANDLE_SET_RETRYING_MESSAGE_PROGRESS_DIALOG = 0x8402;
	private static final int HANDLE_RELOAD_LIST = 0x4517857;
	private static final int HANDLE_START_EVENT_SELECT_ACTIVITY = 0x98742;
	final int HANDLE_SHOW_ATTENDEE_SENDING_PROGRESS = 0x897345;
	final int HANDLE_SET_PROGRESS_BAR_INDETERMINATE = 0x879845;
	private static final int HANDLE_SHOW_CANNOT_SENT_ALERT = 0x897023;

	/**
	 * 
	 */
	private void updateTitle() {
		if(mModeOffline == false) {
			setTitle(mEventData.getName());
		} else {
			setTitle(mEventData.getName() + " - " + getText(R.string.offline));
		}
	}
	
	/**
	 * アラートダイアログの設定
	 * 
	 * @param type
	 */
	private void initProgressDialog(int type) {
		switch (type) {
		case HANDLE_SHOW_ATTENDEE_SENDING_PROGRESS:
			mProgressDialog.setTitle(R.string.attendee_sending_title);
			mProgressDialog.setMessage(AttendeeListActivity.this.getResources()
					.getText(R.string.attendee_sending));
			break;

		}
		;
	}

	/**
	 * アラートダイアログの設定
	 * 
	 * @param type
	 */
	private void initAlertDialog(int type) {
		switch (type) {
		case HANDLE_SHOW_CANNOT_SENT_ALERT:
			mAlertDialog.setTitle(R.string.attendee_info_cannot_sent_title);
			mAlertDialog
					.setMessage(getText(R.string.attendee_info_cannot_sent));
			mAlertDialog.setButton(getText(R.string.ok),
					new Dialog.OnClickListener() {
						@Override
						public void onClick(DialogInterface dialog, int which) {
						}
					});
			break;
		}
		;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see android.app.Activity#onCreate(android.os.Bundle)
	 */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		if (DEBUG_FLG)
			Log.d(LOG_TAG, "onCreate");

		mWakeLock = ((PowerManager)getSystemService(POWER_SERVICE)).newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, AttendeeListActivity.class.getName());
		mWakeLock.acquire();
		
		// 進行中ダイアログ表示
		mHandler.sendEmptyMessage(HANDLE_SHOW_PROGRESS_DIALOG);

		setContentView(R.layout.attendee_list);

		// 検索ボックスの消去
		dismissSearchBox();
		findViewById(R.id.search_layout_btn_exe).setOnClickListener(this);
		((EditText)findViewById(R.id.search_layout_et_term)).setOnEditorActionListener(this);
		
		mLVAttendeeList = (ListView) findViewById(R.id.attendee_list_lv_attendee);
		mLVAttendeeList
				.setOnItemClickListener(mAttendeeListOnItemClickListener);
		mLVAttendeeList
				.setOnItemLongClickListener(mAttendeeListOnItemLongClickListener);

		mProgressDialog = new ProgressDialog(this);
		mProgressDialog.setOnDismissListener(new OnDismissListener() {
			@Override
			public void onDismiss(DialogInterface dialog) {
				mCancelFlg = true;
			}
		});

		// サウンド再生用
		mSoundPlayerSad = MediaPlayer.create(this, R.raw.sad);

		mFilter = new Filter();
		mListAdapter = new ListAdapter(mAttendeeList, mFilter);
		mLVAttendeeList.setAdapter(mListAdapter);

		mAlertDialog = mAlertDialogBuilder.create();

		
		if (DEBUG_FLG)
			Log.d(LOG_TAG, "onCreate completed.");

	}
	private boolean searchBoxIsShown = false;
	private Filter mFilter = null;

	/**
	 * 検索ボックスの表示
	 */
	private final void showSearchBox() {
		searchBoxIsShown = true;
		findViewById(R.id.search_layout).setVisibility(View.VISIBLE);
		findViewById(R.id.search_layout_et_term).requestFocus();
		
	}

	/**
	 * 検索ボックスの非表示
	 */
	private final void dismissSearchBox() {
		searchBoxIsShown = false;
		findViewById(R.id.search_layout).setVisibility(View.GONE);
		
	}

	/*
	 * (non-Javadoc)
	 * @see jp.android_group.attender.android.ui.BaseActivity#onStart()
	 */
	@Override
	public void onStart() {
		super.onStart();
	
		// 初期化処理
		if(mQrScannerLaunchedFlg == false) {
			new Thread(mInitialize).start();
		} else {
			mQrScannerLaunchedFlg = true;
		}
	
	}

	/*
	 * (non-Javadoc)
	 * @see android.app.Activity#onResume()
	 */
	@Override
	public void onPause() {
		super.onPause();
		
		// データをDBに保存
		saveAttendeeData();
	}

		/*
		 * (non-Javadoc)
		 * @see jp.android_group.attender.android.ui.BaseActivity#onStop()
		 */
		@Override
		public void onStop() {
			super.onStop();
			if (DEBUG_FLG)
				Log.d(LOG_TAG, "onPause");
	
			boolean uploadFlg = false;
			synchronized (mAttendeeList) {
				// 更新された出席者情報があるか確認する
				for (Attendee data : mAttendeeList.values()) {
					if (data.getAttendFlg() == Attendee.ATTEND_FLG_ATTEND) {
						uploadFlg = true;
						break;
					}
				}
			}
	
			// 未送信の出席者情報があれば、
			if (mQrScannerLaunchedFlg == false && uploadFlg) {
			//
				if(mModeOffline != true) {
		
					Toast.makeText(
							this,
							this.getResources().getText(R.string.upload_process_was_started),
							Toast.LENGTH_SHORT).show();
		
					// 出席者リストのアップロード
					new Thread(mUploadAttendeeList).start();
		
	//			} else {
	//		
	//				// 情報の保存
	//				saveAttendeeData();
				}
			}
			if (DEBUG_FLG)
				Log.d(LOG_TAG, "onPause completed.");
	
		}

	/*
	 * (non-Javadoc)
	 * @see android.app.Activity#onDestroy()
	 */
	@Override
	public void onDestroy() {
		super.onDestroy();
		mWakeLock.release();
	}
	/**
	 * 
	 * @param attendeeData
	 * @param view
	 */
	private void showAttendCancelDialog(final Attendee attendeeData,
			final View view) {
		AlertDialog ad = mAlertDialogBuilder.create();
		ad.setMessage(getResources().getText(
				R.string.would_you_like_to_undo_attend));
		ad.setTitle(getResources().getText(R.string.undo_attend));
		ad.setButton(getResources().getText(R.string.yes),
				new Dialog.OnClickListener() {
					@Override
					public void onClick(DialogInterface arg0, int arg1) {

						attendeeData.setAttendFlg(Attendee.ATTEND_FLG_NOT_SET);
						synchronized(mDb) {
							Attendee.revertAttend(attendeeData, mDb);
						}
						mListAdapter.notifyDataSetChanged();
						arg0.dismiss();
					}
				});
		ad.setButton2(getResources().getText(R.string.no),
				new Dialog.OnClickListener() {
					@Override
					public void onClick(DialogInterface arg0, int arg1) {
						arg0.dismiss();
					}
				});
		ad.show();
	}

	/**
     * 
     */
	void showDialogCannotCancelAttend() {

		AlertDialog ad = mAlertDialogBuilder.create();
		ad.setTitle(getResources().getText(R.string.undo_attend));
		ad.setMessage(getResources().getText(R.string.you_cannot_undo_attend));
		ad.setButton(getResources().getText(R.string.ok),
				new Dialog.OnClickListener() {
					@Override
					public void onClick(DialogInterface arg0, int arg1) {
						arg0.dismiss();
					}
				});
		ad.show();

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see android.app.Activity#onActivityResult(int, int,
	 * android.content.Intent)
	 */
	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		if (DEBUG_FLG)
			Log.d(LOG_TAG, "onActivityResult");

		switch (requestCode) {

		// イベント情報の設定
		case EventListActivity.REQUEST_CODE:
			if (DEBUG_FLG)
				Log.d(LOG_TAG, "activity result is EventListActivity");

			if (resultCode == Activity.RESULT_OK) {
				if (data.hasExtra(Event.class.getName())) {
					mEventData =
						(Event) data.getSerializableExtra(Event.class.getName());
				}
			}
			break;

		// 認証情報の初期設定
		case ServerSettingActivity.REQUEST_CODE_INIT:
			if (DEBUG_FLG)
				Log.d(LOG_TAG, "activity result is LoginActivity");

			if (resultCode == Activity.RESULT_OK) {
				setResult(Activity.RESULT_CANCELED);
				finish();
			}
			break;

		// ＱＲコードをスキャン
		case REQUEST_CODE_QR_SCAN:

 			if (resultCode == RESULT_OK) {
				String contents = data.getStringExtra("SCAN_RESULT");

				synchronized (mAttendeeList) {
					if (mAttendeeList.containsKey(contents)) {

						// 正常処理の通知
						// TODO: 正常処理の通知を実装
						// 音を鳴らす等

						Attendee attendeeData = (Attendee) mAttendeeList.get(contents);
						if (attendeeData.getAttendFlg() == Attendee.ATTEND_FLG_NOT_SET) {
							attendeeData.setAttendFlg(Attendee.ATTEND_FLG_ATTEND);
							mListAdapter.notifyDataSetChanged();
						}

						// 確認ダイアログの表示
						showDialogConfirm(attendeeData);

						if (DEBUG_FLG)
							Log.d(LOG_TAG, "You confirmed attendee "
									+ mAttendeeList.get(contents)
											.getMailAddress());

					} else {

						mSoundPlayerSad.start();

						// ダイアログ表示：出席予定者リストにない
						showDialogAttendeeNotFoundInList();
					}
				}

			} else if (resultCode == RESULT_CANCELED) {

				// リストの再読み込み
				listReload(mAttendeeList);
			}
			break;
		}

		if (DEBUG_FLG)
			Log.d(LOG_TAG, "onActivityResult completed.");

	}

	// 参加者リストの行をタッチした際の処理
	private OnItemClickListener mAttendeeListOnItemClickListener = new OnItemClickListener() {

		@Override
		public void onItemClick(AdapterView<?> arg0, View view, int position,
				long arg3) {

			Attendee attendeeData = (Attendee) arg0.getItemAtPosition(position);
			switch (attendeeData.getAttendFlg()) {

			case Attendee.ATTEND_FLG_NOT_SET:
				// 出席に変更
				attendeeData.setAttendFlg(Attendee.ATTEND_FLG_ATTEND);
				synchronized(mDb) {
					Attendee.save(attendeeData, mDb);
				}
				mListAdapter.notifyDataSetChanged();
				break;
			};

			if (DEBUG_FLG)
				Log.d(LOG_TAG, "You confirmed attendee "
						+ attendeeData.getMailAddress());
		}
	};

	// 参加者リストの行をロングタッチした際の処理
	private OnItemLongClickListener mAttendeeListOnItemLongClickListener = new OnItemLongClickListener() {

		@Override
		public boolean onItemLongClick(AdapterView<?> arg0, View view,
				int position, long arg3) {

			Attendee attendeeData = (Attendee) arg0
					.getItemAtPosition(position);

			switch (attendeeData.getAttendFlg()) {

			// 参加フラグセット済み
			case Attendee.ATTEND_FLG_ATTEND:
				// ダイアログ表示：参加のキャンセル
				showAttendCancelDialog(attendeeData, view);
				break;
		
			// サーバー送信済み
			case Attendee.ATTEND_FLG_REGISTERED:
				// ダイアログ表示：参加のキャンセルが出来ない
				showDialogCannotCancelAttend();
				break;
			};

			return true;
		}
	};

	/**
	 * 
	 */
	private void showDialogAttendeeNotFoundInList() {
		AlertDialog dialog = mAlertDialogBuilder.create();
		dialog.setTitle(R.string.error_has_occured);
		dialog.setMessage(getResources().getText(
				R.string.it_is_maybe_not_attendee));
		dialog.setButton(getResources().getText(R.string.ok),
				new Dialog.OnClickListener() {
					@Override
					public void onClick(DialogInterface arg0, int arg1) {
						arg0.dismiss();
					}
				});
		dialog.show();
	}

	/**
	 * 確認ダイアログの表示
	 * 
	 * @param data
	 */
	void showDialogConfirm(Attendee data) {

		if (mDialogConfirm == null) {
			mDialogConfirm = new Dialog(this);
			mDialogConfirm.setCancelable(false);
			mDialogConfirm.setTitle(R.string.attend_confirmed);
			mDialogConfirm.setContentView(R.layout.confirm_dialog);

			// 進行表示
			mPbConfirmRemainTime = (ProgressBar) mDialogConfirm
					.findViewById(R.id.confirm_dialog_pb_remain);
			mPbConfirmRemainTime.setMax(10);

			((Button) mDialogConfirm
					.findViewById(R.id.confirm_dialog_btn_continue)).setOnClickListener(new OnClickListener() {
				@Override
				public void onClick(View v) {

					if(mWaitingTimer != null)
						mWaitingTimer.cancel();

					if (mDialogConfirm.isShowing())
						mDialogConfirm.dismiss();

					// QRリーダーの起動
					startQRCodeReader();
				}
			});

		}

		Button btnStop = (Button) mDialogConfirm
				.findViewById(R.id.confirm_dialog_btn_pause);
		btnStop.setText(R.string.pause);
		btnStop.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {

				mPbConfirmRemainTime.setVisibility(View.INVISIBLE);
				Button btn = (Button) v;
				btn.setText(R.string.close);

				// クリックイベントの変更
				btn.setOnClickListener(new OnClickListener() {
					@Override
					public void onClick(View v) {
						if (mDialogConfirm.isShowing())
							mDialogConfirm.dismiss();
					}
				});
				if(mWaitingTimer != null)
					mWaitingTimer.cancel();
				
			}
		});

		mPbConfirmRemainTime.setEnabled(true);
		mPbConfirmRemainTime.setProgress(10);
		mPbConfirmRemainTime.setVisibility(View.VISIBLE);

		((TextView) mDialogConfirm.findViewById(R.id.confirm_dialog_tv_name))
				.setText(data.getName());
		((TextView) mDialogConfirm.findViewById(R.id.confirm_dialog_tv_comment))
				.setText(data.getComment());

		mWaitingTimer = getWaitingTimerTask();
		mTimer.schedule(mWaitingTimer, 0, 1000);
		mDialogConfirm.show();
	}

	TimerTask mWaitingTimer = null;
	private TimerTask getWaitingTimerTask() {

		return new TimerTask() {

			@Override
			public void run() {
				int progress = mPbConfirmRemainTime.getProgress();
				if (progress > 0) {
					mPbConfirmRemainTime.setProgress(progress - 1);
				} else {
					mDialogConfirm.dismiss();

					// タイマのキャンセル
					this.cancel();

					// QRリーダーの起動
					startQRCodeReader();
				}
			}
		};
	}
	
	/**
	 * イベント選択アクティビティ起動
	 */
	private void startEventSelectActivity() {
		Intent intent = new Intent(this, EventListActivity.class);
		intent.addCategory(Intent.CATEGORY_DEFAULT);
		intent.setAction(Intent.ACTION_EDIT);
		startActivityForResult(intent, EventListActivity.REQUEST_CODE);
	}

	/**
	 * 初期化処理
	 */
	Runnable mInitialize = new Runnable() {
		
		public void run() {
			if (DEBUG_FLG)
				Log.d(LOG_TAG, "initializing...");
	
			// サーバー情報
			if (getIntent().hasExtra(ServerEntry.class.getName())) {
				mServerEntry = (ServerEntry) getIntent().getSerializableExtra(
						ServerEntry.class.getName());
				mServerInterface = ServerInterfaceFactory.getInterface(mServerEntry);
			}
			
			// イベント情報
			if (getIntent().hasExtra(Event.class.getName())) {
				mEventData = (Event) getIntent().getSerializableExtra(Event.class.getName());
				mHandler.sendEmptyMessage(HANDLE_UPDATE_TITLE);
			} else {
				// イベント選択アクティビティ起動
				mHandler.sendEmptyMessage(HANDLE_START_EVENT_SELECT_ACTIVITY);
				return;
			}
	
			if(DEBUG_FLG) Log.v(LOG_TAG, "mGetAttendeeData start.");
			new Thread(mGetAttendeeData).start();
	
			if (DEBUG_FLG)
				Log.d(LOG_TAG, "initialize process is completed.");
		}
	};

	/*
	 * (non-Javadoc)
	 * @see jp.android_group.attender.android.ui.BaseActivity#willConfirmWhenClosing()
	 */
	@Override
	protected boolean willConfirmWhenClosing() {
		return true;
	}

	Runnable mGetAttendeeData = new Runnable() {
		public void run() {

			synchronized(mDb) {
				if(mModeOffline != true) {
					mCancelFlg = false;
					
					Log.v(LOG_TAG, "loading attendee data... ");
					
					// 参加者リスト読込み
					for (int i = 0; i < RETRY_NUMBER && mCancelFlg == false; i++) {
						boolean retryFlg = false;
						
						// 参加者リストの読み込み
						try {
							synchronized (mAttendeeList) {
								
								mAttendeeList.clear();
								mAttendeeList.putAll(mServerInterface.getAttendeeData(mEventData));
							}
						} catch (final HttpResponseException e) {
							if (DEBUG_FLG)
								Log.d(LOG_TAG, e.getLocalizedMessage());
//							showToastException(e);
							retryFlg = true;
						} catch (final IOException e) {
//							showToastException(e);
							retryFlg = true;
						}

						if(retryFlg == false) break;

						try {
							if (DEBUG_FLG)
								Log.d(LOG_TAG, "attendee list canot retrieve.");

							mHandler.sendEmptyMessage(HANDLE_SET_RETRYING_MESSAGE_PROGRESS_DIALOG);

							Thread.sleep(RETRY_WAIT_TIME * 1000);
						} catch (final InterruptedException e) {
							showToastException(e);
						}

					}

					if(mCancelFlg == false) {

						// 参加者情報の保存
						saveAttendeeData();
					}
				}
				
				if(mDb.isOpen()) {
					synchronized(mAttendeeList) {
						HashMap<String, Attendee> dbList = Attendee.load(mEventData.getEventCode(), mDb);
						mAttendeeList.putAll(dbList);
						
						listReload(mAttendeeList);
					}
				}
			}
			mHandler.sendEmptyMessage(HANDLE_DISMISS_PROGRESS_DIALOG);
		}
	};

	/**
	 * 参加者情報の保存
	 */
	private void saveAttendeeData() {
		synchronized(mAttendeeList) {
			synchronized(mDb) {
				Log.v(LOG_TAG, "saveAttendeeData start");
				if(mDb.isOpen() && mDb.inTransaction() == false) {
					mDb.beginTransaction();
					try {
						for(Attendee data : mAttendeeList.values()) {
							data.setEventId(mEventData.getEventCode());
							Attendee.save(data, mDb);
						}
						// 
						mEventData.setLastRetrieveDate(Calendar.getInstance().getTimeInMillis());
						Event.save(mEventData, mDb);
						mDb.setTransactionSuccessful();
					} finally {
						mDb.endTransaction();
					}
				}
			}
			Log.v(LOG_TAG, "saveAttendeeData end");
		}
	}
	/*
	 * (non-Javadoc)
	 * 
	 * @see android.app.Activity#onTrackballEvent(android.view.MotionEvent)
	 */
	@Override
	public boolean onTrackballEvent(MotionEvent event) {

		if (event.getAction() == MotionEvent.ACTION_DOWN) {
			if (DEBUG_FLG)
				Log.d(LOG_TAG, "trackball is pushed.");

			// ＱＲコードリーダ起動
			startQRCodeReader();

			return true;
		}

		return super.onTrackballEvent(event);
	}

	/*
	 * (non-Javadoc)
	 * @see android.app.Activity#onKeyDown(int, android.view.KeyEvent)
	 */
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {

		switch(event.getKeyCode()) {
		case KeyEvent.KEYCODE_BACK :
			if(searchBoxIsShown) {
				dismissSearchBox();
				return false;
			}
			break;
		case KeyEvent.KEYCODE_SEARCH :
			if(searchBoxIsShown) {
				dismissSearchBox();
			} else {
				showSearchBox();
			}
			return true;
		};
		
		return super.onKeyDown(keyCode, event);
	}

	/**
	 * リストの再読込
	 * 
	 * @param list
	 */
	private synchronized void listReload(HashMap<String, Attendee> list) {
		synchronized (mListAdapter) {
			mListAdapter.list.clear();
			mListAdapter.list.addAll(list.values());
		}

		mHandler.sendEmptyMessage(HANDLE_RELOAD_LIST);
	}

	/*
	 * (non-Javadoc)
	 * @see jp.android_group.attender.android.ui.BaseActivity#changeModeOffline(boolean)
	 */
	@Override
	protected void changeModeOffline(boolean mode) {
		// TODO Auto-generated method stub
		
	}

	/*
	 * (non-Javadoc)
	 * @see android.app.Activity#onPrepareOptionsMenu(android.view.Menu)
	 */
	@Override
	public boolean onPrepareOptionsMenu(Menu menu) {
		menu.findItem(R.id.attendee_list_menu_commit).setEnabled(!(mModeOffline));

		MenuItem item = menu.findItem(R.id.attendee_list_menu_search);
		if(searchBoxIsShown) {
			item.setTitle(getText(R.string.search_dismiss));
		} else {
			item.setTitle(getText(R.string.search));
		}
		
		return super.onPrepareOptionsMenu(menu);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see android.app.Activity#onCreateOptionsMenu(android.view.Menu)
	 */
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.attendee_list, menu);

		MenuItem menuItem = null;

		menuItem = menu.findItem(R.id.attendee_list_menu_commit);
		menuItem.setOnMenuItemClickListener(new OnMenuItemClickListener() {
			@Override
			public boolean onMenuItemClick(MenuItem item) {
				Toast.makeText(AttendeeListActivity.this, getText(R.string.upload_process_was_started), Toast.LENGTH_LONG).show();

				// 出席者リストのアップロード
				if(mModeOffline != true) {
					new Thread(mUploadAttendeeList).start();
				}

				return true;
			}
		});

		menuItem = menu.findItem(R.id.attendee_list_menu_search);
    		menuItem.setOnMenuItemClickListener(new OnMenuItemClickListener() {
			@Override
			public boolean onMenuItemClick(MenuItem item) {
				if(searchBoxIsShown) {
					dismissSearchBox();
				} else {
					showSearchBox();
				}
				return true;
			}
		});

		menuItem = menu.findItem(R.id.attendee_list_menu_sort_number);
		menuItem.setOnMenuItemClickListener(new OnMenuItemClickListener() {
			@Override
			public boolean onMenuItemClick(MenuItem item) {
				setSortComparator(ID_ASC);
				return true;
			}
		});

		menuItem = menu.findItem(R.id.attendee_list_menu_sort_name);
		menuItem.setOnMenuItemClickListener(new OnMenuItemClickListener() {
			@Override
			public boolean onMenuItemClick(MenuItem item) {
				setSortComparator(NAME_ASC);
				return true;
			}
		});

		menuItem = menu.findItem(R.id.attendee_list_menu_sort_mail_address);
		menuItem.setOnMenuItemClickListener(new OnMenuItemClickListener() {
			@Override
			public boolean onMenuItemClick(MenuItem item) {
				
				if (DEBUG_FLG)
					Log.d(LOG_TAG, "sort as mail address.");
				setSortComparator(MAIL_ADDRESS_ASC);
				return true;
			}
		}); 

		menuItem = menu.findItem(R.id.attendee_list_menu_setting);
		menuItem.setOnMenuItemClickListener(new OnMenuItemClickListener() {
			@Override
			public boolean onMenuItemClick(MenuItem item) {

				AttendeeListActivity.this
						.setResult(RESULT_LAUNCH_LOGIN_SETTING);
				AttendeeListActivity.this.finish();

				return true;
			}
		});

		menuItem = menu.findItem(R.id.attendee_list_menu_about);
		menuItem.setOnMenuItemClickListener(new OnMenuItemClickListener() {
			@Override
			public boolean onMenuItemClick(MenuItem item) {

				showAboutDialog();
				return false;
			}
		});

		return super.onCreateOptionsMenu(menu);
	}

	// 並び替え順序
	private Comparator<Attendee> mComparator = NAME_ASC;

	private void setSortComparator(Comparator<Attendee> comp) {
		mComparator = comp;
		mListAdapter.setComparetor(mComparator);
	}

	// メールアドレス - 昇順
	private static final Comparator<Attendee> MAIL_ADDRESS_ASC = new MailAddressComparatorAsc();

	private static class MailAddressComparatorAsc implements
			Comparator<Attendee> {
		@Override
		public int compare(Attendee object1, Attendee object2) {
			return object1.getMailAddress().compareTo(object2.getMailAddress());
		}
	}

	// メールアドレス - 降順
	private static final Comparator<Attendee> MAIL_ADDRESS_DESC = new MailAddressComparatorDesc();

	private static class MailAddressComparatorDesc implements
			Comparator<Attendee> {
		@Override
		public int compare(Attendee object1, Attendee object2) {
			return object1.getMailAddress().compareTo(object2.getMailAddress())
					* -1;
		}
	}

	// 名前 - 昇順
	private static final Comparator<Attendee> NAME_ASC = new NameComparatorAsc();

	private static class NameComparatorAsc implements Comparator<Attendee> {
		@Override
		public int compare(Attendee object1, Attendee object2) {
			return object1.getName().compareTo(object2.getName());
		}
	}

	// 名前 - 降順
	private static final Comparator<Attendee> NAME_DESC = new NameComparatorDesc();

	private static class NameComparatorDesc implements Comparator<Attendee> {
		@Override
		public int compare(Attendee object1, Attendee object2) {
			return object1.getName().compareTo(object2.getName()) * -1;
		}
	}

	// ＩＤ - 昇順
	private static final Comparator<Attendee> ID_ASC = new IdComparatorAsc();

	private static class IdComparatorAsc implements Comparator<Attendee> {
		@Override
		public int compare(Attendee object1, Attendee object2) {
			if(object1.getId() == object2.getId()) {
				return 0;
			} else if(object1.getId() < object2.getId()) {
				return -1;
			} else {
				return 1;
			}
		}
	}

	// ＩＤ - 降順
	private static final Comparator<Attendee> ID_DESC = new IdComparatorDesc();

	private static class IdComparatorDesc implements Comparator<Attendee> {
		@Override
		public int compare(Attendee object1, Attendee object2) {
			if(object1.getId() == object2.getId()) {
				return 0;
			} else if(object1.getId() < object2.getId()) {
				return 1;
			} else {
				return -1;
			}
		}
	}

	private static class Filter {
		private String email = null;
		
		/**
		 * @param email the email to set
		 */
		public void setEmail(String email) {
			this.email = email;
		}

		/**
		 * @return the email
		 */
		public String getEmail() {
			return email;
		}

		/**
		 * 
		 * @param data
		 * @return
		 */
		private boolean exec(Attendee data) {
			if(email == null) return true;
			if(data.getMailAddress().indexOf(email) == -1) return false;

			return true;
		}
	}

	/**
	 * リストアダプタ
	 */
	class ListAdapter extends BaseAdapter {

		private final HashMap<String, Attendee> origList;
		private final ArrayList<Attendee> list;
		private Filter filter;

		ListAdapter(HashMap<String, Attendee> val, Filter filter) {
			origList = val;
			list = new ArrayList<Attendee>(val.values());
			this.filter = filter;
		}

		/**
		 * Comparetorの設定
		 * 
		 * @param comp
		 */
		public void setComparetor(Comparator<Attendee> comp) {
			Collections.sort(list, comp);
			mHandler.sendEmptyMessage(HANDLE_RELOAD_LIST);

		}

		
		/**
		 * 
		 * @param filter
		 */
		public void updateFilter() {
			new Thread(updateFilter).start();
		}
		
		private Runnable updateFilter = new Runnable() {
			@Override
			public void run() {
				synchronized(list) {
					list.clear();
					Collection<Attendee> attendeeList = origList.values();				
					for(Attendee data : attendeeList) {
						if(filter.exec(data)) list.add(data);
					}
				}
				mHandler.sendEmptyMessage(HANDLE_RELOAD_LIST);
			}
		};

		@Override
		public int getCount() {
			if (list != null) {
				return list.size();
			} else {
				return 0;
			}
		}

		@Override
		public Object getItem(int position) {
			if (list != null) {
				return list.get(position);
			} else {
				return null;
			}
		}

		@Override
		public long getItemId(int position) {
			return position;
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {

			ViewHolder viewHolder;
			Attendee attendeeData = (Attendee) getItem(position);

			if (convertView == null) {
				convertView = View.inflate(AttendeeListActivity.this,
						R.layout.attendee_list_row, null);

				viewHolder = new ViewHolder();
//				viewHolder.attendeeNumber = (TextView) convertView
//						.findViewById(R.id.attendee_list_row_tv_attendee_number);
				viewHolder.attendeeName = (TextView) convertView
						.findViewById(R.id.attendee_list_row_tv_attendee_name);
				viewHolder.attendeeMailAddress = (TextView) convertView
						.findViewById(R.id.attendee_list_row_tv_attendee_mail_address);
				viewHolder.attendeeStatus = (TextView) convertView
						.findViewById(R.id.attendee_list_row_tv_attenee_status);

				convertView.setTag(viewHolder);
			} else {
				viewHolder = (ViewHolder) convertView.getTag();
			}

			String number = String.valueOf(getText(R.string.status_not_set));
			if (attendeeData.getNumber() != null) {
				number = attendeeData.getNumber();
			} else {
				number = attendeeData.getNumber();
			}
//			viewHolder.attendeeNumber.setText(number);

			viewHolder.attendeeName.setText(attendeeData.getName());
			viewHolder.attendeeMailAddress.setText(attendeeData
					.getMailAddress());

			CharSequence status = null;
			if (attendeeData.getAttendFlg() == Attendee.ATTEND_FLG_NOT_SET) {
				status = getText(R.string.status_not_set);
			} else if (attendeeData.getAttendFlg() == Attendee.ATTEND_FLG_ATTEND) {
				status = getText(R.string.status_attend);
			} else if (attendeeData.getAttendFlg() == Attendee.ATTEND_FLG_REGISTERED) {
				status = getText(R.string.status_reistered);
			}
			viewHolder.attendeeStatus.setText(status);

			return convertView;
		}

	}

	private class ViewHolder {

//		TextView attendeeNumber = null;
		TextView attendeeName = null;
		TextView attendeeMailAddress = null;
		TextView attendeeStatus = null;

	}

	/*
	 * (non-Javadoc)
	 * @see android.view.View.OnClickListener#onClick(android.view.View)
	 */
	@Override
	public void onClick(View v) {
		switch(v.getId()) {
		case R.id.search_layout_btn_exe :
			mFilter.setEmail(((TextView)findViewById(R.id.search_layout_et_term)).getText().toString());
			mListAdapter.updateFilter();
			hideKeyboard();

			break;
		};
	}

	/**
	 * 
	 */
	private final void hideKeyboard() {
		InputMethodManager ims = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
		IBinder token = findViewById(R.id.search_layout_et_term).getWindowToken();
		ims.hideSoftInputFromWindow(token, InputMethodManager.HIDE_NOT_ALWAYS);
	}

	/*
	 * (non-Javadoc)
	 * @see android.widget.TextView.OnEditorActionListener#onEditorAction(android.widget.TextView, int, android.view.KeyEvent)
	 */
	@Override
	public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {

		if(v.getText().length() > 0) {
			mFilter.setEmail(v.getText().toString());
			mListAdapter.updateFilter();

			hideKeyboard();
			return true;
		}
		
		return false;
	}

}